package com.javasm.config;

import com.alibaba.fastjson.JSONObject;
import com.javasm.service.LoginUserService;
import com.javasm.service.MyUsernamePasswordAuthenticationFilter;
import com.javasm.service.PhoneAuthenticationProvider;
import com.javasm.service.PhoneNumAuthenticationFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;

@Configuration
public class SecurityConfig1 extends WebSecurityConfigurerAdapter {
    /**
     * 声明一个 PasswordEncoder ，这样数据库存储的密码就不需要 "{加密方式}"，这样的前缀
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Bean
    public MyUsernamePasswordAuthenticationFilter myUsernamePasswordAuthenticationFilter() throws Exception {
        MyUsernamePasswordAuthenticationFilter filter = new MyUsernamePasswordAuthenticationFilter();
        filter.setAuthenticationManager(authenticationManagerBean());//认证使用
        //设置登陆成功返回值是json
        filter.setAuthenticationSuccessHandler(new AuthenticationSuccessHandler() {
            @Override
            public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
                response.setContentType("application/json;charset=utf-8");
                PrintWriter out = response.getWriter();
                out.write(JSONObject.toJSONString(authentication));
                out.flush();
                out.close();
            }
        });
        //设置登陆失败返回值是json
        filter.setAuthenticationFailureHandler(new AuthenticationFailureHandler() {
            @Override
            public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
                response.setContentType("application/json;charset=utf-8");
                PrintWriter out = response.getWriter();
                Map<String,String> map = new HashMap<>();
                map.put("errMsg", exception.getMessage());
                out.write(JSONObject.toJSONString(map));
                out.flush();
                out.close();
            }
        });
        //设置登录接口
        filter.setFilterProcessesUrl("/user/login");
        return filter;
    }
//
//    @Override
//    protected void configure(HttpSecurity http) throws Exception {
//        http.authorizeRequests()
//                .anyRequest().authenticated()
//                .and().csrf().disable();
//        //把自定义认证过滤器加到拦截器链中
//        http.addFilterAt(myUsernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
//    }



    @Bean
    public PhoneNumAuthenticationFilter phoneNumAuthenticationFilter() throws Exception {
        PhoneNumAuthenticationFilter filter = new PhoneNumAuthenticationFilter();
        filter.setAuthenticationManager(authenticationManagerBean());//认证使用
        //设置登陆成功返回值是json
        filter.setAuthenticationSuccessHandler(new AuthenticationSuccessHandler() {
            @Override
            public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
                response.setContentType("application/json;charset=utf-8");
                PrintWriter out = response.getWriter();
                out.write(JSONObject.toJSONString(authentication));
                out.flush();
                out.close();
            }
        });
        //设置登陆失败返回值是json
        filter.setAuthenticationFailureHandler(new AuthenticationFailureHandler() {
            @Override
            public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
                response.setContentType("application/json;charset=utf-8");
                PrintWriter out = response.getWriter();
                Map<String,String> map = new HashMap<>();
                map.put("errMsg", "手机登陆失败："+ exception.getMessage());
                out.write(JSONObject.toJSONString(map));
                out.flush();
                out.close();
            }
        });
        filter.setFilterProcessesUrl("/phone/login");//其实这里不用设置，在 PhoneNumAuthenticationFilter 我们已经定义了一个静态变量
        return filter;
    }

    @Autowired
    private LoginUserService loginUserService;
    /**
     * DaoAuthenticationProvider 是默认做账户密码认证的，现在有两种登录方式，手机号和账户密码
     * 如果不在这里声明，账户密码登录不能用
     * @return
     */
    @Bean
    public DaoAuthenticationProvider daoAuthenticationProvider() {
        DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
        //对默认的UserDetailsService进行覆盖
        authenticationProvider.setUserDetailsService(loginUserService);
        authenticationProvider.setPasswordEncoder(passwordEncoder());
        return authenticationProvider;
    }

    @Bean
    public RoleHierarchy roleHierarchy() {
        RoleHierarchyImpl hierarchy = new RoleHierarchyImpl();
        //让 admin 继承 user 的权限
        hierarchy.setHierarchy("ROLE_admin > ROLE_user");
        return hierarchy;
    }
    @Autowired
    private PhoneAuthenticationProvider phoneAuthenticationProvider;
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/admin/hello").hasAuthority("admin.hello")//设置具体的权限
                .antMatchers("/admin/**").hasRole("admin")// /admin开头的请求，admin角色都可以访问
                .antMatchers("/user/**").hasRole("user")//  /user开头的请求，user角色都可以访问
                // /phone/code 请求不用登陆
                .antMatchers("/code", "/phone/code").permitAll()
                .anyRequest().authenticated()
                .and().csrf().disable();

        //自定义没有权限的返回结果
        http.exceptionHandling().accessDeniedHandler(new AccessDeniedHandler() {
            @Override
            public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
                response.setContentType("application/json;charset=utf-8");
                PrintWriter out = response.getWriter();
                Map<String,String> map = new HashMap<>();
                map.put("errMsg", "没有权限");
                out.write(JSONObject.toJSONString(map));
                out.flush();
                out.close();
               }
            });

        //账号密码验证
        http.addFilterAt(myUsernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
                .authenticationProvider(daoAuthenticationProvider());//把账户密码验证加进去

        //把 手机号认证过滤器 加到拦截器链中
        http.addFilterAfter(phoneNumAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
                .authenticationProvider(phoneAuthenticationProvider);//把验证逻辑加进去

    }

}